30天鐵人賽即將進入尾聲啦~今天來提一個很常用的資料儲存格式:JSON。
JSON(JavaScript Object Notation)是一種由Key(屬性)和Value(屬性值)所組成的資料格式,格式與使用哪種程式語言無關,許多程式語言都能夠將其解析和字串化,其廣泛使用的程度也使其成為通用的資料格式。
每筆資料的呈現方式說明如下:
{}
包住,資料屬性與名稱(字串)用雙引號""
括住,中間用冒號:
隔開。bool
或數字型別,則不用雙引號。ex:{"Name":"王大華","Age":18,"IsMarried":false}
,如果有經過排版通常會將每組Key/Value分行顯示,例如下面這樣:{
"Name":"王大華",
"Age":18,
"IsMarried":false
}
[]
包住,每筆資料間用逗點
隔開,例如:[
{
"Name":"王大華",
"Age":18,
"IsMarried":false
},
{
"Name":"陳小原",
"Age":43,
"IsMarried":true
}
]
了解JSON資料格式之後,接著我們就可以來對資料做一些處理。
從上面JSON陣列格式範例來看,每筆資料有相同的Key(屬性名稱)與對應的Value(值),用C#語言的角度來看就是物件的形式。所以我們假如想要將JSON資料轉成C#程式碼來使用,第一件事就是要依照JSON屬性來建立C#的類別。例如上面的格式我們可以建立一個Member
類別如下:
public class Member
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsMarried { get; set; }
}
但如果每筆資料的屬性非常多的話,要一個一個KEY似乎不是有效率的方式,比如點入下方JSON連結來看,這是一個台北市Youbike站點相關的資料,沒有排版的情況下相信光要找有哪些屬性眼睛就脫窗了= =
https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json
因此我們可以透過Visual Studio內建的功能,使用更有效率的方式來轉換,前置作業先新增一個ASP.NET Web應用程式(.NET Framework)
專案,並選擇MVC範本
建立,這邊應該前面做到快爛掉了吧~還不會就趕緊翻前面文章看看啦!
建立完成後操作步驟如下:
在Model
資料夾新增一個類別(ex:Youbike
),並將class內容清空
將連結的JSON內容全選複製,在類別內選擇性貼上JSON來源
貼上後Code應該會如下面:
public class Rootobject
{
public Class1[] Property1 { get; set; }
}
public class Class1
{
public string sno { get; set; }
public string sna { get; set; }
public int tot { get; set; }
public int sbi { get; set; }
public string sarea { get; set; }
public string mday { get; set; }
public float lat { get; set; }
public float lng { get; set; }
public string ar { get; set; }
public string sareaen { get; set; }
public string snaen { get; set; }
public string aren { get; set; }
public int bemp { get; set; }
public string act { get; set; }
public string srcUpdateTime { get; set; }
public string updateTime { get; set; }
public string infoTime { get; set; }
public string infoDate { get; set; }
}
Class1
名稱修改成自訂名稱,例如Youbike
,上面Rootobject
類別表示資料最頂層由一個陣列[]
包住底下每筆資料,後續不會用到所以是可以刪除的,修改後Code如下:namespace JsonDemoWebsite.Models
{
public class Youbike
{
public string sno { get; set; }
public string sna { get; set; }
public int tot { get; set; }
public int sbi { get; set; }
public string sarea { get; set; }
public string mday { get; set; }
public float lat { get; set; }
public float lng { get; set; }
public string ar { get; set; }
public string sareaen { get; set; }
public string snaen { get; set; }
public string aren { get; set; }
public int bemp { get; set; }
public string act { get; set; }
public string srcUpdateTime { get; set; }
public string updateTime { get; set; }
public string infoTime { get; set; }
public string infoDate { get; set; }
}
}
以下的範例會使用Newtonsoft.Json
這個套件來轉換JSON資料,首先打開NuGet套件管理員確認是否已經有安裝該套件,沒有的話就先安裝。
接著說明如何獲取網址連結的JSON資料,首先Client端要發出HTTP請求到Server端,Server端收到請求後會回應JSON資料內容,再由Client端接收。
接收到資料後,透過套件幫助將JSON格式轉換成我們要的C#語言內容,這段稱為「反序列化(Deserialize)」。此範例我們會將資料轉換成List<Youbike>
型別,拿到List
物件後,後續就和之前講過的方式一樣,可以作為model傳入View顯示等等。
了解後我們就來實作Code吧!步驟參考如下:
於Model
資料夾新增一個類別名為JsonService
,建立後將class
宣告為static
,方便後續不用再new物件出來。
在JsonService
內加入以下Code,這裡建立一個非同步方法來獲取JSON資料,並反序列化成List<Youbike>
物件回傳。
public static async Task<List<Youbike>> GetDataAsync_Youbike()
{
List<Youbike> result = new List<Youbike>();
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Get;
request.RequestUri = new Uri("https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json");
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<List<Youbike>>(responseBody);
}
return result;
}
Controller
資料夾內新建控制器,名為JsonController
,將內建Index()
方法修改為下列Code: public class JsonController : Controller
{
// GET: Json
public async Task<ActionResult> Index() //using System.Threading.Tasks;
{
var model = await JsonService.GetDataAsync_Youbike();
return View(model);
}
}
新增對應的View內容,這邊使用List
範本與Youbike
模型快速建立。
建立後Code與顯示頁面如下,可以比照前面幾天的範例去美化或調整顯示內容,這邊就不再贅述了。
@model IEnumerable<JsonDemoWebsite.Models.Youbike>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.sno)
</th>
<th>
@Html.DisplayNameFor(model => model.sna)
</th>
<th>
@Html.DisplayNameFor(model => model.tot)
</th>
<th>
@Html.DisplayNameFor(model => model.sbi)
</th>
<th>
@Html.DisplayNameFor(model => model.sarea)
</th>
<th>
@Html.DisplayNameFor(model => model.mday)
</th>
<th>
@Html.DisplayNameFor(model => model.lat)
</th>
<th>
@Html.DisplayNameFor(model => model.lng)
</th>
<th>
@Html.DisplayNameFor(model => model.ar)
</th>
<th>
@Html.DisplayNameFor(model => model.sareaen)
</th>
<th>
@Html.DisplayNameFor(model => model.snaen)
</th>
<th>
@Html.DisplayNameFor(model => model.aren)
</th>
<th>
@Html.DisplayNameFor(model => model.bemp)
</th>
<th>
@Html.DisplayNameFor(model => model.act)
</th>
<th>
@Html.DisplayNameFor(model => model.srcUpdateTime)
</th>
<th>
@Html.DisplayNameFor(model => model.updateTime)
</th>
<th>
@Html.DisplayNameFor(model => model.infoTime)
</th>
<th>
@Html.DisplayNameFor(model => model.infoDate)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.sno)
</td>
<td>
@Html.DisplayFor(modelItem => item.sna)
</td>
<td>
@Html.DisplayFor(modelItem => item.tot)
</td>
<td>
@Html.DisplayFor(modelItem => item.sbi)
</td>
<td>
@Html.DisplayFor(modelItem => item.sarea)
</td>
<td>
@Html.DisplayFor(modelItem => item.mday)
</td>
<td>
@Html.DisplayFor(modelItem => item.lat)
</td>
<td>
@Html.DisplayFor(modelItem => item.lng)
</td>
<td>
@Html.DisplayFor(modelItem => item.ar)
</td>
<td>
@Html.DisplayFor(modelItem => item.sareaen)
</td>
<td>
@Html.DisplayFor(modelItem => item.snaen)
</td>
<td>
@Html.DisplayFor(modelItem => item.aren)
</td>
<td>
@Html.DisplayFor(modelItem => item.bemp)
</td>
<td>
@Html.DisplayFor(modelItem => item.act)
</td>
<td>
@Html.DisplayFor(modelItem => item.srcUpdateTime)
</td>
<td>
@Html.DisplayFor(modelItem => item.updateTime)
</td>
<td>
@Html.DisplayFor(modelItem => item.infoTime)
</td>
<td>
@Html.DisplayFor(modelItem => item.infoDate)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
有的時候網路上的JSON資料不見得都是只有一層結構,例如下方連結是交通部觀光局提供的觀光景點JSON檔案。
https://gis.taiwan.net.tw/XMLReleaseALL_public/scenic_spot_C_f.json
從內容可以看到最外層是"XML_Head"
屬性,在"XML_Head"
裡面有"Listname"
、"Language"
、"Orgname"
、"Updatetime"
、"Infos"
,而實際上每筆景點的資料是在"Infos"
裡面的"Info"
當中。當我們將資料全選複製並選擇性貼上類別時,會產生如下面Code:
public class Rootobject
{
public XML_Head XML_Head { get; set; }
}
public class XML_Head
{
public string Listname { get; set; }
public string Language { get; set; }
public string Orgname { get; set; }
public DateTime Updatetime { get; set; }
public Infos Infos { get; set; }
}
public class Infos
{
public Info[] Info { get; set; }
}
public class Info
{
public string Id { get; set; }
public string Name { get; set; }
public string Zone { get; set; }
public string Toldescribe { get; set; }
public string Description { get; set; }
public string Tel { get; set; }
public string Add { get; set; }
public string Zipcode { get; set; }
public string Region { get; set; }
public string Town { get; set; }
public string Travellinginfo { get; set; }
public string Opentime { get; set; }
public string Picture1 { get; set; }
public string Picdescribe1 { get; set; }
public string Picture2 { get; set; }
public string Picdescribe2 { get; set; }
public string Picture3 { get; set; }
public string Picdescribe3 { get; set; }
public string Map { get; set; }
public string Gov { get; set; }
public float Px { get; set; }
public float Py { get; set; }
public string Orgclass { get; set; }
public string Class1 { get; set; }
public string Class2 { get; set; }
public string Class3 { get; set; }
public string Level { get; set; }
public string Website { get; set; }
public string Parkinginfo { get; set; }
public float? Parkinginfo_Px { get; set; }
public float? Parkinginfo_Py { get; set; }
public string Ticketinfo { get; set; }
public string Remarks { get; set; }
public string Keyword { get; set; }
public DateTime? Changetime { get; set; }
}
可以看到開發工具會逐層產生對應的類別與屬性,我們比照上面範例新建一個方法來請求資料,這時候回傳的內容就不會像Youbike範例那麼單純了,需要抽絲剝繭般找到下層真正要的資料,作法參考下面Code,可以與Youbike範例比較在取得responseBody
後將資料反序列化的差異。Controller和View的呈現就不多提,重點在於如何取得正確資料。
public static async Task<List<TravelPlace>> GetDataAsync_TravelPlace()
{
List<TravelPlace> result = new List<TravelPlace>();
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Get;
request.RequestUri = new Uri("https://gis.taiwan.net.tw/XMLReleaseALL_public/scenic_spot_C_f.json");
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
//先反序列化成JSON物件
var JsonObject = JsonConvert.DeserializeObject<JObject>(responseBody);
//抓取Info裡面的資料並轉換成<List<TravelPlace>>物件
var token = JsonObject["XML_Head"]["Infos"]["Info"];
result = token.ToObject<List<TravelPlace>>();
}
return result;
}
最後再提供一個範例參考,這個範例會稍微將View頁面美化過,另外因為資料屬性有string
也有string[]
,在處理View的時候也要特別注意,當套用預設範本時是不會自動生成string[]
相關HTML標籤的,得自行處理加入內容。
完整做法請參考下方Code,也可以先自行練習看看如何呈現,與上述兩個範例差異不大,所以這邊就不再贅述。
public class Rootobject
{
public BusTaxi[] Property1 { get; set; }
}
public class BusTaxi
{
public string name { get; set; }
public string company { get; set; }
public string[] tel { get; set; }
public string imgSrc { get; set; }
public string[] zone { get; set; }
public string[] route { get; set; }
public string[] length { get; set; }
public string[] shift { get; set; }
}
public class BusTaxi
{
[DisplayName("路線")]
public string name { get; set; }
[DisplayName("車隊")]
public string company { get; set; }
[DisplayName("連絡電話")]
public string[] tel { get; set; }
[DisplayName("圖片")]
public string imgSrc { get; set; }
[DisplayName("地區")]
public string[] zone { get; set; }
[DisplayName("起訖站")]
public string[] route { get; set; }
[DisplayName("路線長度")]
public string[] length { get; set; }
[DisplayName("班次")]
public string[] shift { get; set; }
}
public static async Task<List<BusTaxi>> GetDataAsync_BusTaxi()
{
List<BusTaxi> result = new List<BusTaxi>();
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
var url = "https://data.kcg.gov.tw/dataset/82ef292a-5e96-4c99-a1ca-acae712e3805/resource/482fbc04-8666-4031-8dcf-13b5b4d35860/download/bustaxi.json";
request.Method = HttpMethod.Get;
request.RequestUri = new Uri(url);
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<List<BusTaxi>>(responseBody);
}
return result;
}
public async Task<ActionResult> BusTaxi()
{
var model = await JSONService.GetDataAsync_BusTaxi();
return View(model);
}
@model IEnumerable<JsonDemoWebsite.Models.BusTaxi>
@{
ViewBag.Title = "BusTaxi";
}
<h2>BusTaxi</h2>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.name)
</th>
<th>
@Html.DisplayNameFor(model => model.company)
</th>
<th>
@Html.DisplayNameFor(model => model.imgSrc)
</th>
<th>
@Html.DisplayNameFor(model => model.zone)
</th>
<th>
@Html.DisplayNameFor(model => model.route)
</th>
<th>
@Html.DisplayNameFor(model => model.length)
</th>
<th>
@Html.DisplayNameFor(model => model.shift)
</th>
<th>
@Html.DisplayNameFor(model => model.tel)
</th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.name)
</td>
<td>
@Html.DisplayFor(modelItem => item.company)
</td>
<td>
<a href="@item.imgSrc" target="_blank"><img src="@item.imgSrc" style="width:200px;height:150px;" /></a>
</td>
<td>
@foreach (var arrayItem in item.zone)
{
<div>
@arrayItem <br />
</div>
}
</td>
<td>
@foreach (var arrayItem in item.route)
{
<div>
@arrayItem <br />
</div>
}
</td>
<td>
@foreach (var arrayItem in item.length)
{
<div>
@arrayItem <br />
</div>
}
</td>
<td>
@foreach (var arrayItem in item.shift)
{
<div>
@arrayItem <br />
</div>
}
</td>
<td>
@foreach (var arrayItem in item.tel)
{
<div>
@arrayItem <br />
</div>
}
</td>
</tr>
}
</table>
那麼今天就介紹到這邊啦~明天會接續今天內容來介紹好用的套件:PagedList
,See you Tomorrow~